// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

#include "google/protobuf/compiler/csharp/csharp_message.h"

#include <algorithm>
#include <sstream>

#include "google/protobuf/compiler/code_generator.h"
#include "absl/container/flat_hash_map.h"
#include "absl/log/absl_log.h"
#include "absl/strings/str_cat.h"
#include "google/protobuf/compiler/csharp/csharp_doc_comment.h"
#include "google/protobuf/compiler/csharp/csharp_enum.h"
#include "google/protobuf/compiler/csharp/csharp_field_base.h"
#include "google/protobuf/compiler/csharp/csharp_helpers.h"
#include "google/protobuf/compiler/csharp/csharp_options.h"
#include "google/protobuf/compiler/csharp/names.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/descriptor.pb.h"
#include "google/protobuf/io/printer.h"
#include "google/protobuf/wire_format.h"
#include "google/protobuf/wire_format_lite.h"

// Must be last.
#include "google/protobuf/port_def.inc"

namespace google
{
  namespace protobuf
  {
    namespace compiler
    {
      namespace csharp
      {

        bool CompareFieldNumbers(const FieldDescriptor *d1, const FieldDescriptor *d2)
        {
          return d1->number() < d2->number();
        }

        MessageGenerator::MessageGenerator(const Descriptor *descriptor,
                                           const Options *options)
            : SourceGeneratorBase(options),
              descriptor_(descriptor),
              has_bit_field_count_(0),
              end_tag_(GetGroupEndTag(descriptor)),
              has_extension_ranges_(descriptor->extension_range_count() > 0)
        {
          // fields by number
          for (int i = 0; i < descriptor_->field_count(); i++)
          {
            fields_by_number_.push_back(descriptor_->field(i));
          }
          std::sort(fields_by_number_.begin(), fields_by_number_.end(),
                    CompareFieldNumbers);

          int presence_bit_count = 0;
          for (int i = 0; i < descriptor_->field_count(); i++)
          {
            const FieldDescriptor *field = descriptor_->field(i);
            if (RequiresPresenceBit(field))
            {
              presence_bit_count++;
              if (has_bit_field_count_ == 0 || (presence_bit_count % 32) == 0)
              {
                has_bit_field_count_++;
              }
            }
          }
        }

        MessageGenerator::~MessageGenerator() {}

        std::string MessageGenerator::class_name() { return descriptor_->name(); }

        std::string MessageGenerator::full_class_name()
        {
          return GetClassName(descriptor_);
        }

        const std::vector<const FieldDescriptor *> &
        MessageGenerator::fields_by_number()
        {
          return fields_by_number_;
        }

        void MessageGenerator::AddDeprecatedFlag(io::Printer *printer)
        {
          if (descriptor_->options().deprecated())
          {
            printer->Print("[global::System.ObsoleteAttribute]\n");
          }
        }

        void MessageGenerator::AddSerializableAttribute(io::Printer *printer)
        {
          if (this->options()->serializable)
          {
            printer->Print("[global::System.SerializableAttribute]\n");
          }
        }

        void MessageGenerator::Generate(io::Printer *printer)
        {
          absl::flat_hash_map<absl::string_view, std::string> vars;
          vars["class_name"] = class_name();
          vars["access_level"] = class_access_level();

          WriteMessageDocComment(printer, descriptor_);
          AddDeprecatedFlag(printer);
          AddSerializableAttribute(printer);

          // printer->Print(
          //     "[global::System.Diagnostics.DebuggerDisplayAttribute(\"{ToString(),nq}"
          //     "\")]\n");
          printer->Print(vars, "$access_level$ sealed partial class $class_name$ : ");

          if (has_extension_ranges_)
          {
            printer->Print(vars, "pb::IExtendableMessage<$class_name$>\n");
          }
          else
          {
            printer->Print(vars, "pb::IMessage<$class_name$>\n");
          }
          // printer->Print("#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE\n");
          // printer->Print("    , pb::IBufferMessage\n");
          // printer->Print("#endif\n");
          printer->Print("{\n");
          printer->Indent();

          // All static fields and properties
          printer->Print(
              vars,
              "private static readonly pb::MessageParser<$class_name$> _parser = new "
              "pb::MessageParser<$class_name$>(() => new $class_name$());\n");

          printer->Print("private pb::UnknownFieldSet _unknownFields;\n");

          if (has_extension_ranges_)
          {
            if (IsDescriptorProto(descriptor_->file()))
            {
              printer->Print(vars,
                             // CustomOptions compatibility
                             "internal pb::ExtensionSet<$class_name$> _extensions;\n");
            }
            else
            {
              printer->Print(vars,
                             "private pb::ExtensionSet<$class_name$> _extensions;\n");
            }

            // a read-only property for fast
            // retrieval of the set in IsInitialized
            printer->Print(vars,
                           "private pb::ExtensionSet<$class_name$> _Extensions { get { "
                           "return _extensions; } }\n");
          }

          for (int i = 0; i < has_bit_field_count_; i++)
          {
            // don't use arrays since all arrays are heap allocated, saving allocations
            // use ints instead of bytes since bytes lack bitwise operators, saving
            // casts
            printer->Print("private int _hasBits$i$;\n", "i", absl::StrCat(i));
          }

          // 查找第一个字段是否是data_list,然后自定义Parser 方便读取
          const FieldDescriptor *fieldDescriptor = descriptor_->field(0);
          if (fieldDescriptor->type() == FieldDescriptor::TYPE_MESSAGE && fieldDescriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE && fieldDescriptor->name() == "data_list")
          {
            //
            std::unique_ptr<FieldGeneratorBase> generator(
                CreateFieldGeneratorInternal(fieldDescriptor));

            vars["childCfg"] = fieldDescriptor->message_type()->name();

            // vars["childCfg"] =vars["class_name"].substr(0,vars["class_name"].length() - 4);//字符串操作

            if (fieldDescriptor->message_type()->FindFieldByName("id"))
            {
              printer->Print(
                  vars,
                  "public static ConfigParserData<$class_name$,$childCfg$> parserData = new() {\n"
                  "   parseFrom = _parser.ParseFrom,\n"
                  "   getDataList = (obj) => obj.DataList,\n"
                  "   getIdFunc = (obj) => obj.Id,\n"
                  "   fileName = \"$childCfg$_pb\"\n"
                  "};\n\n");
            }
            else // 无id 操作
            {
              printer->Print(
                  vars,
                  "public static ConfigParserData<$class_name$,$childCfg$> parserData = new() {\n"
                  "   parseFrom = _parser.ParseFrom,\n"
                  "   getDataList = (obj) => obj.DataList,\n"
                  "   fileName = \"$childCfg$_pb\"\n"
                  "};\n\n");
            }
          }
          else
          {
            WriteGeneratedCodeAttributes(printer);
            printer->Print(
                vars,
                "public static pb::MessageParser<$class_name$> Parser { get { return _parser; } }\n\n");
          }

          // Access the message descriptor via the relevant file descriptor or
          // containing message descriptor.
          if (!descriptor_->containing_type())
          {
            vars["descriptor_accessor"] =
                absl::StrCat(GetReflectionClassName(descriptor_->file()),
                             ".Descriptor.MessageTypes[", descriptor_->index(), "]");
          }
          else
          {
            vars["descriptor_accessor"] =
                absl::StrCat(GetClassName(descriptor_->containing_type()),
                             ".Descriptor.NestedTypes[", descriptor_->index(), "]");
          }

          // WriteGeneratedCodeAttributes(printer);
          // printer->Print(vars,
          //                "public static pbr::MessageDescriptor Descriptor {\n"
          //                "  get { return $descriptor_accessor$; }\n"
          //                "}\n"
          //                "\n");
          // WriteGeneratedCodeAttributes(printer);
          // printer->Print(vars,
          //                "pbr::MessageDescriptor pb::IMessage.Descriptor {\n"
          //                "  get { return Descriptor; }\n"
          //                "}\n"
          //                "\n");

          // Parameterless constructor and partial OnConstruction method.
          WriteGeneratedCodeAttributes(printer);
          printer->Print(vars,
                         "public $class_name$() {\n"
                         "  OnConstruction();\n"
                         "}\n\n"
                         "partial void OnConstruction();\n\n");

          GenerateCloningCode(printer);
          GenerateFreezingCode(printer);

          // Fields/properties
          for (int i = 0; i < descriptor_->field_count(); i++)
          {
            const FieldDescriptor *fieldDescriptor = descriptor_->field(i);

            // Rats: we lose the debug comment here :(
            printer->Print(
                "/// <summary>Field number for the \"$field_name$\" field.</summary>\n"
                "public const int $field_constant_name$ = $index$;\n",
                "field_name", fieldDescriptor->name(), "field_constant_name",
                GetFieldConstantName(fieldDescriptor), "index",
                absl::StrCat(fieldDescriptor->number()));
            std::unique_ptr<FieldGeneratorBase> generator(
                CreateFieldGeneratorInternal(fieldDescriptor));
            generator->GenerateMembers(printer);
            printer->Print("\n");
          }

          // oneof properties (for real oneofs, which come before synthetic ones)
          for (int i = 0; i < descriptor_->real_oneof_decl_count(); i++)
          {
            const OneofDescriptor *oneof = descriptor_->oneof_decl(i);
            vars["name"] = UnderscoresToCamelCase(oneof->name(), false);
            vars["property_name"] = UnderscoresToCamelCase(oneof->name(), true);
            vars["original_name"] = oneof->name();
            printer->Print(vars,
                           "private object $name$_;\n"
                           "/// <summary>Enum of possible cases for the "
                           "\"$original_name$\" oneof.</summary>\n"
                           "public enum $property_name$OneofCase {\n");
            printer->Indent();
            printer->Print("None = 0,\n");
            for (int j = 0; j < oneof->field_count(); j++)
            {
              const FieldDescriptor *field = oneof->field(j);
              printer->Print("$oneof_case_name$ = $index$,\n", "oneof_case_name",
                             GetOneofCaseName(field), "index",
                             absl::StrCat(field->number()));
            }
            printer->Outdent();
            printer->Print("}\n");
            // TODO: Should we put the oneof .proto comments here?
            // It's unclear exactly where they should go.
            printer->Print(vars,
                           "private $property_name$OneofCase $name$Case_ = "
                           "$property_name$OneofCase.None;\n");
            WriteGeneratedCodeAttributes(printer);
            printer->Print(vars,
                           "public $property_name$OneofCase $property_name$Case {\n"
                           "  get { return $name$Case_; }\n"
                           "}\n\n");
            WriteGeneratedCodeAttributes(printer);
            printer->Print(vars,
                           "public void Clear$property_name$() {\n"
                           "  $name$Case_ = $property_name$OneofCase.None;\n"
                           "  $name$_ = null;\n"
                           "}\n\n");
          }

          // Standard methods
          GenerateFrameworkMethods(printer);
          GenerateMessageSerializationMethods(printer);
          GenerateMergingMethods(printer);

          if (has_extension_ranges_)
          {
            printer->Print(
                vars,
                "public TValue GetExtension<TValue>(pb::Extension<$class_name$, "
                "TValue> extension) {\n"
                "  return pb::ExtensionSet.Get(ref _extensions, extension);\n"
                "}\n"
                "public pbc::RepeatedField<TValue> "
                "GetExtension<TValue>(pb::RepeatedExtension<$class_name$, TValue> "
                "extension) {\n"
                "  return pb::ExtensionSet.Get(ref _extensions, extension);\n"
                "}\n"
                "public pbc::RepeatedField<TValue> "
                "GetOrInitializeExtension<TValue>(pb::RepeatedExtension<$class_name$, "
                "TValue> extension) {\n"
                "  return pb::ExtensionSet.GetOrInitialize(ref _extensions, "
                "extension);\n"
                "}\n"
                "public void SetExtension<TValue>(pb::Extension<$class_name$, TValue> "
                "extension, TValue value) {\n"
                "  pb::ExtensionSet.Set(ref _extensions, extension, value);\n"
                "}\n"
                "public bool HasExtension<TValue>(pb::Extension<$class_name$, TValue> "
                "extension) {\n"
                "  return pb::ExtensionSet.Has(ref _extensions, extension);\n"
                "}\n"
                "public void ClearExtension<TValue>(pb::Extension<$class_name$, "
                "TValue> extension) {\n"
                "  pb::ExtensionSet.Clear(ref _extensions, extension);\n"
                "}\n"
                "public void "
                "ClearExtension<TValue>(pb::RepeatedExtension<$class_name$, TValue> "
                "extension) {\n"
                "  pb::ExtensionSet.Clear(ref _extensions, extension);\n"
                "}\n\n");
          }

          // Nested messages and enums
          if (HasNestedGeneratedTypes())
          {
            printer->Print(vars,
                           "#region Nested types\n"
                           "/// <summary>Container for nested types declared in the "
                           "$class_name$ message type.</summary>\n");
            WriteGeneratedCodeAttributes(printer);
            printer->Print("public static partial class Types {\n");
            printer->Indent();
            for (int i = 0; i < descriptor_->enum_type_count(); i++)
            {
              EnumGenerator enumGenerator(descriptor_->enum_type(i), this->options());
              enumGenerator.Generate(printer);
            }
            for (int i = 0; i < descriptor_->nested_type_count(); i++)
            {
              // Don't generate nested types for maps...
              if (!IsMapEntryMessage(descriptor_->nested_type(i)))
              {
                MessageGenerator messageGenerator(descriptor_->nested_type(i),
                                                  this->options());
                messageGenerator.Generate(printer);
              }
            }
            printer->Outdent();
            printer->Print(
                "}\n"
                "#endregion\n"
                "\n");
          }

          if (descriptor_->extension_count() > 0)
          {
            printer->Print(vars,
                           "#region Extensions\n"
                           "/// <summary>Container for extensions for other messages "
                           "declared in the $class_name$ message type.</summary>\n");
            WriteGeneratedCodeAttributes(printer);
            printer->Print("public static partial class Extensions {\n");
            printer->Indent();
            for (int i = 0; i < descriptor_->extension_count(); i++)
            {
              std::unique_ptr<FieldGeneratorBase> generator(
                  CreateFieldGeneratorInternal(descriptor_->extension(i)));
              generator->GenerateExtensionCode(printer);
            }
            printer->Outdent();
            printer->Print(
                "}\n"
                "#endregion\n"
                "\n");
          }

          printer->Outdent();
          printer->Print("}\n");
          printer->Print("\n");
        }

        // Helper to work out whether we need to generate a class to hold nested
        // types/enums. Only tricky because we don't want to generate map entry types.
        bool MessageGenerator::HasNestedGeneratedTypes()
        {
          if (descriptor_->enum_type_count() > 0)
          {
            return true;
          }
          for (int i = 0; i < descriptor_->nested_type_count(); i++)
          {
            if (!IsMapEntryMessage(descriptor_->nested_type(i)))
            {
              return true;
            }
          }
          return false;
        }

        void MessageGenerator::GenerateCloningCode(io::Printer *printer)
        {
          absl::flat_hash_map<absl::string_view, std::string> vars;
          WriteGeneratedCodeAttributes(printer);
          vars["class_name"] = class_name();
          printer->Print(vars, "public $class_name$($class_name$ other) : this() {\n");
          printer->Indent();
          for (int i = 0; i < has_bit_field_count_; i++)
          {
            printer->Print("_hasBits$i$ = other._hasBits$i$;\n", "i", absl::StrCat(i));
          }
          // Clone non-oneof fields first (treating optional proto3 fields as non-oneof)
          for (int i = 0; i < descriptor_->field_count(); i++)
          {
            const FieldDescriptor *field = descriptor_->field(i);
            if (field->real_containing_oneof())
            {
              continue;
            }
            std::unique_ptr<FieldGeneratorBase> generator(
                CreateFieldGeneratorInternal(field));
            generator->GenerateCloningCode(printer);
          }
          // Clone just the right field for each real oneof
          for (int i = 0; i < descriptor_->real_oneof_decl_count(); ++i)
          {
            const OneofDescriptor *oneof = descriptor_->oneof_decl(i);
            vars["name"] = UnderscoresToCamelCase(oneof->name(), false);
            vars["property_name"] = UnderscoresToCamelCase(oneof->name(), true);
            printer->Print(vars, "switch (other.$property_name$Case) {\n");
            printer->Indent();
            for (int j = 0; j < oneof->field_count(); j++)
            {
              const FieldDescriptor *field = oneof->field(j);
              std::unique_ptr<FieldGeneratorBase> generator(
                  CreateFieldGeneratorInternal(field));
              vars["oneof_case_name"] = GetOneofCaseName(field);
              printer->Print(vars,
                             "case $property_name$OneofCase.$oneof_case_name$:\n");
              printer->Indent();
              generator->GenerateCloningCode(printer);
              printer->Print("break;\n");
              printer->Outdent();
            }
            printer->Outdent();
            printer->Print("}\n\n");
          }
          // Clone unknown fields
          printer->Print(
              "_unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);\n");
          if (has_extension_ranges_)
          {
            printer->Print(
                "_extensions = pb::ExtensionSet.Clone(other._extensions);\n");
          }

          printer->Outdent();
          printer->Print("}\n\n");

          WriteGeneratedCodeAttributes(printer);
          printer->Print(vars,
                         "public $class_name$ Clone() {\n"
                         "  return new $class_name$(this);\n"
                         "}\n\n");
        }

        void MessageGenerator::GenerateFreezingCode(io::Printer *printer) {}

        void MessageGenerator::GenerateFrameworkMethods(io::Printer *printer)
        {
          absl::flat_hash_map<absl::string_view, std::string> vars;
          vars["class_name"] = class_name();

          // Equality
          WriteGeneratedCodeAttributes(printer);
          printer->Print(vars,
                         "public override bool Equals(object other) {\n"
                         "  return Equals(other as $class_name$);\n"
                         "}\n\n");
          WriteGeneratedCodeAttributes(printer);
          printer->Print(vars,
                         "public bool Equals($class_name$ other) {\n"
                         "  if (ReferenceEquals(other, null)) {\n"
                         "    return false;\n"
                         "  }\n"
                         "  if (ReferenceEquals(other, this)) {\n"
                         "    return true;\n"
                         "  }\n");
          printer->Indent();
          for (int i = 0; i < descriptor_->field_count(); i++)
          {
            std::unique_ptr<FieldGeneratorBase> generator(
                CreateFieldGeneratorInternal(descriptor_->field(i)));
            generator->WriteEquals(printer);
          }
          for (int i = 0; i < descriptor_->real_oneof_decl_count(); i++)
          {
            printer->Print(
                "if ($property_name$Case != other.$property_name$Case) return false;\n",
                "property_name",
                UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), true));
          }
          if (has_extension_ranges_)
          {
            printer->Print(
                "if (!Equals(_extensions, other._extensions)) {\n"
                "  return false;\n"
                "}\n");
          }
          printer->Outdent();
          printer->Print(
              "  return Equals(_unknownFields, other._unknownFields);\n"
              "}\n\n");

          // GetHashCode
          // Start with a non-zero value to easily distinguish between null and "empty"
          // messages.
          WriteGeneratedCodeAttributes(printer);
          printer->Print(
              "public override int GetHashCode() {\n"
              "  int hash = 1;\n");
          printer->Indent();
          for (int i = 0; i < descriptor_->field_count(); i++)
          {
            std::unique_ptr<FieldGeneratorBase> generator(
                CreateFieldGeneratorInternal(descriptor_->field(i)));
            generator->WriteHash(printer);
          }
          for (int i = 0; i < descriptor_->real_oneof_decl_count(); i++)
          {
            printer->Print(
                "hash ^= (int) $name$Case_;\n", "name",
                UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), false));
          }
          if (has_extension_ranges_)
          {
            printer->Print(
                "if (_extensions != null) {\n"
                "  hash ^= _extensions.GetHashCode();\n"
                "}\n");
          }
          printer->Print(
              "if (_unknownFields != null) {\n"
              "  hash ^= _unknownFields.GetHashCode();\n"
              "}\n"
              "return hash;\n");
          printer->Outdent();
          printer->Print("}\n\n");

          // WriteGeneratedCodeAttributes(printer);
          // printer->Print(
          //     "public override string ToString() {\n"
          //     "  return pb::JsonFormatter.ToDiagnosticString(this);\n"
          //     "}\n\n");
        }

        void MessageGenerator::GenerateMessageSerializationMethods(
            io::Printer *printer)
        {
          WriteGeneratedCodeAttributes(printer);
          printer->Print("public void WriteTo(pb::CodedOutputStream output) {\n");
          // printer->Print("#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE\n");
          // printer->Indent();
          // printer->Print("output.WriteRawMessage(this);\n");
          // printer->Outdent();
          // printer->Print("#else\n");
          printer->Indent();
          GenerateWriteToBody(printer, false);
          printer->Outdent();
          // printer->Print("#endif\n");
          printer->Print("}\n\n");

          // printer->Print("#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE\n");
          // WriteGeneratedCodeAttributes(printer);
          // printer->Print(
          //     "void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) "
          //     "{\n");
          // printer->Indent();
          // GenerateWriteToBody(printer, true);
          // printer->Outdent();
          // printer->Print("}\n");
          // printer->Print("#endif\n\n");

          WriteGeneratedCodeAttributes(printer);
          printer->Print("public int CalculateSize() {\n");
          printer->Indent();
          printer->Print("int size = 0;\n");
          for (int i = 0; i < descriptor_->field_count(); i++)
          {
            std::unique_ptr<FieldGeneratorBase> generator(
                CreateFieldGeneratorInternal(descriptor_->field(i)));
            generator->GenerateSerializedSizeCode(printer);
          }

          if (has_extension_ranges_)
          {
            printer->Print(
                "if (_extensions != null) {\n"
                "  size += _extensions.CalculateSize();\n"
                "}\n");
          }

          printer->Print(
              "if (_unknownFields != null) {\n"
              "  size += _unknownFields.CalculateSize();\n"
              "}\n");

          printer->Print("return size;\n");
          printer->Outdent();
          printer->Print("}\n\n");
        }

        void MessageGenerator::GenerateWriteToBody(io::Printer *printer,
                                                   bool use_write_context)
        {
          // Serialize all the fields
          for (int i = 0; i < fields_by_number().size(); i++)
          {
            std::unique_ptr<FieldGeneratorBase> generator(
                CreateFieldGeneratorInternal(fields_by_number()[i]));
            generator->GenerateSerializationCode(printer, use_write_context);
          }

          if (has_extension_ranges_)
          {
            // Serialize extensions
            printer->Print(use_write_context ? "if (_extensions != null) {\n"
                                               "  _extensions.WriteTo(ref output);\n"
                                               "}\n"
                                             : "if (_extensions != null) {\n"
                                               "  _extensions.WriteTo(output);\n"
                                               "}\n");
          }

          // Serialize unknown fields
          printer->Print(use_write_context ? "if (_unknownFields != null) {\n"
                                             "  _unknownFields.WriteTo(ref output);\n"
                                             "}\n"
                                           : "if (_unknownFields != null) {\n"
                                             "  _unknownFields.WriteTo(output);\n"
                                             "}\n");

          // TODO: Memoize size of frozen messages?
        }

        void MessageGenerator::GenerateMergingMethods(io::Printer *printer)
        {
          // Note:  These are separate from GenerateMessageSerializationMethods()
          //   because they need to be generated even for messages that are optimized
          //   for code size.
          absl::flat_hash_map<absl::string_view, std::string> vars;
          vars["class_name"] = class_name();

          WriteGeneratedCodeAttributes(printer);
          printer->Print(vars, "public void MergeFrom($class_name$ other) {\n");
          printer->Indent();
          printer->Print(
              "if (other == null) {\n"
              "  return;\n"
              "}\n");
          // Merge non-oneof fields, treating optional proto3 fields as normal fields
          for (int i = 0; i < descriptor_->field_count(); i++)
          {
            const FieldDescriptor *field = descriptor_->field(i);
            if (field->real_containing_oneof())
            {
              continue;
            }
            std::unique_ptr<FieldGeneratorBase> generator(
                CreateFieldGeneratorInternal(field));
            generator->GenerateMergingCode(printer);
          }
          // Merge oneof fields (for non-synthetic oneofs)
          for (int i = 0; i < descriptor_->real_oneof_decl_count(); ++i)
          {
            const OneofDescriptor *oneof = descriptor_->oneof_decl(i);
            vars["name"] = UnderscoresToCamelCase(oneof->name(), false);
            vars["property_name"] = UnderscoresToCamelCase(oneof->name(), true);
            printer->Print(vars, "switch (other.$property_name$Case) {\n");
            printer->Indent();
            for (int j = 0; j < oneof->field_count(); j++)
            {
              const FieldDescriptor *field = oneof->field(j);
              vars["oneof_case_name"] = GetOneofCaseName(field);
              printer->Print(vars,
                             "case $property_name$OneofCase.$oneof_case_name$:\n");
              printer->Indent();
              std::unique_ptr<FieldGeneratorBase> generator(
                  CreateFieldGeneratorInternal(field));
              generator->GenerateMergingCode(printer);
              printer->Print("break;\n");
              printer->Outdent();
            }
            printer->Outdent();
            printer->Print("}\n\n");
          }
          // Merge extensions
          if (has_extension_ranges_)
          {
            printer->Print(
                "pb::ExtensionSet.MergeFrom(ref _extensions, other._extensions);\n");
          }

          // Merge unknown fields.
          printer->Print(
              "_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, "
              "other._unknownFields);\n");

          printer->Outdent();
          printer->Print("}\n\n");

          WriteGeneratedCodeAttributes(printer);
          printer->Print("public void MergeFrom(pb::CodedInputStream input) {\n");
          // printer->Print("#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE\n");
          // printer->Indent();
          // printer->Print("input.ReadRawMessage(this);\n");
          // printer->Outdent();
          // printer->Print("#else\n");
          printer->Indent();
          GenerateMainParseLoop(printer, false);
          printer->Outdent();
          // printer->Print("#endif\n");
          printer->Print("}\n\n");

          // printer->Print("#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE\n");
          // WriteGeneratedCodeAttributes(printer);
          // printer->Print(
          //     "void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) "
          //     "{\n");
          // printer->Indent();
          // GenerateMainParseLoop(printer, true);
          // printer->Outdent();
          // printer->Print("}\n");  // method
          // printer->Print("#endif\n\n");
        }

        void MessageGenerator::GenerateMainParseLoop(io::Printer *printer,
                                                     bool use_parse_context)
        {
          absl::flat_hash_map<absl::string_view, std::string> vars;
          vars["maybe_ref_input"] = use_parse_context ? "ref input" : "input";

          printer->Print(
              "uint tag;\n"
              "while ((tag = input.ReadTag()) != 0) {\n"
              "  switch(tag) {\n");
          printer->Indent();
          printer->Indent();
          if (end_tag_ != 0)
          {
            printer->Print(
                "case $end_tag$:\n"
                "  return;\n",
                "end_tag", absl::StrCat(end_tag_));
          }
          if (has_extension_ranges_)
          {
            printer->Print(vars,
                           "default:\n"
                           "  if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, "
                           "$maybe_ref_input$)) {\n"
                           "    _unknownFields = "
                           "pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, "
                           "$maybe_ref_input$);\n"
                           "  }\n"
                           "  break;\n");
          }
          else
          {
            printer->Print(
                vars,
                "default:\n"
                "  _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, "
                "$maybe_ref_input$);\n"
                "  break;\n");
          }
          for (int i = 0; i < fields_by_number().size(); i++)
          {
            const FieldDescriptor *field = fields_by_number()[i];
            internal::WireFormatLite::WireType wt =
                internal::WireFormat::WireTypeForFieldType(field->type());
            uint32_t tag = internal::WireFormatLite::MakeTag(field->number(), wt);
            // Handle both packed and unpacked repeated fields with the same Read*Array
            // call; the two generated cases are the packed and unpacked tags.
            // TODO: Check that is_packable is equivalent to
            // is_repeated && wt in { VARINT, FIXED32, FIXED64 }.
            // It looks like it is...
            if (field->is_packable())
            {
              printer->Print("case $packed_tag$:\n", "packed_tag",
                             absl::StrCat(internal::WireFormatLite::MakeTag(
                                 field->number(),
                                 internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED)));
            }

            printer->Print("case $tag$: {\n", "tag", absl::StrCat(tag));
            printer->Indent();
            std::unique_ptr<FieldGeneratorBase> generator(
                CreateFieldGeneratorInternal(field));
            generator->GenerateParsingCode(printer, use_parse_context);
            printer->Print("break;\n");
            printer->Outdent();
            printer->Print("}\n");
          }
          printer->Outdent();
          printer->Print("}\n"); // switch
          printer->Outdent();
          printer->Print("}\n"); // while
        }

        // it's a waste of space to track presence for all values, so we only track them
        // if they're not nullable
        int MessageGenerator::GetPresenceIndex(const FieldDescriptor *descriptor)
        {
          if (!RequiresPresenceBit(descriptor))
          {
            return -1;
          }

          int index = 0;
          for (int i = 0; i < fields_by_number().size(); i++)
          {
            const FieldDescriptor *field = fields_by_number()[i];
            if (field == descriptor)
            {
              return index;
            }
            if (RequiresPresenceBit(field))
            {
              index++;
            }
          }
          ABSL_DLOG(FATAL) << "Could not find presence index for field "
                           << descriptor->name();
          return -1;
        }

        FieldGeneratorBase *MessageGenerator::CreateFieldGeneratorInternal(
            const FieldDescriptor *descriptor)
        {
          return CreateFieldGenerator(descriptor, GetPresenceIndex(descriptor),
                                      this->options());
        }

      } // namespace csharp
    }   // namespace compiler
  }     // namespace protobuf
} // namespace google

#include "google/protobuf/port_undef.inc"
